package rmmk.algorithms.features.global;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import rmmk.framework.Utils;
import rmmk.framework.datasources.Document;

public class GlobalClassesWordCount {

	private static Logger logger = LoggerFactory
			.getLogger(GlobalClassesWordCount.class);

	private static HashMap<String, HashMap<String, Integer>> map = new HashMap<String, HashMap<String, Integer>>();

	public GlobalClassesWordCount(){
		map.clear();
	}
	
	public void process(List<Document> documents) {

		logger.info("GlobalClassesWordCount started");

		Map<String, HashMap<String, Integer>> syncedMap = Collections
				.synchronizedMap(map);
		List<Document> syncedDocs = Collections.synchronizedList(documents);

		ArrayList<Thread> threads = new ArrayList<Thread>();

		int cores = Utils.getProcessorsCount();

		for (int i = 0; i < cores; ++i) {
			Thread t = new Thread(new GlobalClassessWordCountRunner(cores, i,
					syncedMap, syncedDocs));
			t.start();
			threads.add(t);
		}

		for (Thread t : threads)
			try {
				t.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		logger.info("GLobalClassesWordCount finished");
	}

	private static HashMap<String, ArrayList<String>> cache = null;

	@SuppressWarnings("unchecked")
	public static HashMap<String, ArrayList<String>> getMap(int size) {
		if (cache != null)
			return cache;

		HashMap<String, ArrayList<String>> ret = new HashMap<String, ArrayList<String>>();

		while (!checkForUniqness(ret, size)) {

			ret.clear();

			for (Entry<String, HashMap<String, Integer>> entry : map.entrySet()) {
				HashMap<String, Integer> wordCount = entry.getValue();

				ArrayList<String> list = new ArrayList<String>();

				int added = 0;
				HashMap<String, Integer> wordCountClone = (HashMap<String, Integer>) wordCount
						.clone();

				while (added++ < size) {
					Entry<String, Integer> theChoosenOne = null;
					Iterator<Entry<String, Integer>> iter = wordCountClone
							.entrySet().iterator();
					theChoosenOne = iter.next();

					while (iter.hasNext()) {
						Entry<String, Integer> current = iter.next();

						if (current.getValue() > theChoosenOne.getValue())
							theChoosenOne = current;
					}

					//logger.info("Pair: " + theChoosenOne.getKey() + " for "
						//	+ entry.getKey());
					list.add(theChoosenOne.getKey());
					wordCountClone.remove(theChoosenOne.getKey());
				}

				ret.put(entry.getKey(), list);
			}
		}

		return cache = ret;
	}

	static int limit = 0;

	private static boolean checkForUniqness(
			HashMap<String, ArrayList<String>> whatWeFound, int size) {

		if (whatWeFound.size() > 0)
			return true;

		if (whatWeFound.size() == 0)
			return false;

		for (Entry<String, HashMap<String, Integer>> e : map.entrySet())
			if (e.getValue().size() == size)
				return true;

		for (Entry<String, ArrayList<String>> entry : whatWeFound.entrySet()) {
			for (Entry<String, ArrayList<String>> secondOne : whatWeFound
					.entrySet())
				if (entry.getKey() != secondOne.getKey()) {
					// sprawdzamy wartości
					for (String one : entry.getValue())
						for (String another : entry.getValue())
							if (one.equals(another)) {
								// usuwamy z mapy to słowo, aby więcej go nie
								// używać

								for (Entry<String, HashMap<String, Integer>> ent : map
										.entrySet())
									if (ent.getValue().containsKey(another)
											&& limit++ < size
													* whatWeFound.size()) {
										ent.getValue().remove(another);

										return false;
									} else
										return true;
							}
				}
		}

		return true;
	}

	private class GlobalClassessWordCountRunner implements Runnable {

		private int cores, shift;
		private Map<String, HashMap<String, Integer>> syncedMap = null;
		private List<Document> documents = null;

		public GlobalClassessWordCountRunner(int cores, int shift,
				Map<String, HashMap<String, Integer>> syncedMap2,
				List<Document> documents) {
			this.cores = cores;
			this.shift = shift;
			this.syncedMap = syncedMap2;
			this.documents = documents;
		}

		@Override
		public void run() {
			for (int i = shift; i < this.documents.size(); i += cores) {
				HashMap<String, Integer> classMap = null;
				synchronized (syncedMap) {
					if (syncedMap.containsKey(documents.get(i).toString()))
						classMap = syncedMap.get(documents.get(i).toString());
					else {
						classMap = new HashMap<String, Integer>();
						syncedMap.put(documents.get(i).toString(), classMap);
					}
				}

				for (String word : this.documents.get(i).getExtractedWords()) {
					updateMap(word, classMap);
				}
			}
		}

		boolean contains = false;
		Integer count = 0;

		private void updateMap(String word, Map<String, Integer> classMap) {

			contains = false;
			count = 0;
			// if(classMap == null)
			// synchronized(classMap){
			// if(classMap == null)
			// classMap = new HashMap<String, Integer>();
			// }

			synchronized (classMap) {
				contains = classMap.containsKey(word);
			}

			if (contains) {
				synchronized (classMap) {
					count = classMap.get(word);
					classMap.put(word, ++count);
				}
			} else
				synchronized (classMap) {
					classMap.put(word, count);
				}
		}

	}
}
